package com.dhl.fin.api.service;

import com.dhl.fin.api.common.dto.QueryDto;
import com.dhl.fin.api.common.service.CommonService;
import com.dhl.fin.api.common.util.CollectorUtil;
import com.dhl.fin.api.common.util.DateUtil;
import com.dhl.fin.api.common.util.ObjectUtil;
import com.dhl.fin.api.constant.RemindConstant;
import com.dhl.fin.api.dao.MyRemindDao;
import com.dhl.fin.api.domain.ExtraJobCron;
import com.dhl.fin.api.domain.MensesRemindTask;
import com.dhl.fin.api.domain.RemindTask;
import com.dhl.fin.api.dto.NextTimeDTO;
import com.dhl.fin.api.util.CronJobUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author CuiJianbo
 * @since 2025/5/6
 */
@Service
public class ExtraJobCronService extends CommonService<ExtraJobCron> {

    @Autowired
    private MyRemindDao myRemindDao;

    @Autowired
    private CronJobService cronJobService;

    @Autowired
    private MensesRemindTaskService mensesRemindTaskService;

    public List<ExtraJobCron> getAllExtraJobCron() throws Exception {
        return select(QueryDto.builder()
                .available()
                .addJoinDomain("remindTask")
                .addWhere("execute_time > now()")
                .addWhere("is_active = 1")
                .build());
    }

    public Map<Long, List<ExtraJobCron>> selectExtraJobFromRemindTask(List<Long> taskIds) throws Exception {
        String ids = CollectorUtil.join(taskIds, ",");

        return select(QueryDto.builder()
                .available()
                .addJoinDomain("remindTask")
                .addWhere("remind_task_id in (" + ids + ")")
                .addWhere("extra_job_cron.execute_time > now()")
                .addOrder("extra_job_cron.execute_time asc")
                .build())
                .stream()
                .collect(Collectors.groupingBy(v -> v.getRemindTask().getId()));

    }

    public List<ExtraJobCron> getExtraJobCronByTaskId(Long taskId) throws Exception {
        return select(QueryDto.builder()
                .available()
                .addWhere("execute_time > now()")
                .addWhere("remind_task_id = " + taskId)
                .build());
    }

    public ExtraJobCron getFirstExtraJobCronByRemindTaskId(Long taskId) throws Exception {
        return get(QueryDto.builder()
                .available()
                .addWhere("execute_time > now()")
                .addWhere("menses_remind_task_id = " + taskId)
                .build());
    }

    public void deleteJobByTaskId(long taskId) throws Exception {
        myRemindDao.deleteExtraJobByTaskId(taskId);
    }

    public void deleteJobById(long id) throws Exception {
        myRemindDao.deleteExtraJobById(id);
    }

    public void cancelExtraJob(long taskId) throws Exception {
        select(QueryDto.builder()
                .available()
                .addJoinDomain("remindTask")
                .addWhere("remind_task.id=" + taskId)
                .build())
                .forEach(p -> {
                    CronJobUtil.disableCronJob(p.getScheduleIdNum());
                });
        myRemindDao.deleteExtraJobByTaskId(taskId);
    }

    public void cancelMensesExtraJob(long taskId) throws Exception {
        select(QueryDto.builder()
                .available()
                .addJoinDomain("mensesRemindTask")
                .addWhere("menses_remind_task.id=" + taskId)
                .build())
                .forEach(p -> {
                    CronJobUtil.disableCronJob(p.getScheduleIdNum());
                });
        myRemindDao.deleteMensesExtraJobByTaskId(taskId);
    }


    /**
     * 如果启用了每次重复提醒，就要生成定时任务
     */
    public void generateExtraJob(RemindTask remindTask) throws Exception {

        Date nextExecuteTime = remindTask.getNextRemindDateTime(DateUtil.getSysDate());

        ExtraJobCron extraJobCron = new ExtraJobCron();
        extraJobCron.setExecuteTime(new Timestamp(nextExecuteTime.getTime()));
        extraJobCron.setCron(CronJobUtil.dateToCron(nextExecuteTime));
        extraJobCron.setTargetTime(extraJobCron.getExecuteTime());
        extraJobCron.setRemindTask(remindTask);
        extraJobCron.setRemark(RemindConstant.ON_TIME_TIP);

        //添加额外的定时提醒的数据
        saveOrUpdate(extraJobCron);

        //添加额外的定时提醒的定时任务
        String scheduleTaskId = cronJobService.addCronJob(CronJobUtil.dateToCron(nextExecuteTime), remindTask, extraJobCron);

        //更新scheduleIdNum
        updateFieldById(extraJobCron.getId(), "scheduleIdNum", scheduleTaskId);

        String durationTime;
        if (remindTask.getIsMoreTip()) {
            durationTime = remindTask.getDurationTime();
            addCycleJob(durationTime, remindTask, false);
        }

        if (remindTask.getIsPrevTip()) {
            durationTime = remindTask.getPrevDurationTime();
            addCycleJob(durationTime, remindTask, true);
        }

    }

    /**
     * 生理期的周期任务
     */
    public void generateMensesExtraJob(MensesRemindTask mensesRemindTask) throws Exception {
        List<NextTimeDTO> nextTimeDTOList = mensesRemindTaskService.getMensesCalenderData(mensesRemindTask).getNextTimes();
        for (NextTimeDTO nextTimeDTO : nextTimeDTOList) {
            Timestamp dateTime = DateUtil.getFullTimestamp(nextTimeDTO.getNextTime());
            String cronExpress = CronJobUtil.dateToCron(dateTime);
            ExtraJobCron extraJobCron = new ExtraJobCron(null, nextTimeDTO, mensesRemindTask);

            //添加额外的定时提醒的数据
            saveOrUpdate(extraJobCron);

            //添加额外的定时提醒的定时任务
            String scheduleTaskId = cronJobService.addCronJob(cronExpress, mensesRemindTask, extraJobCron);

            //更新scheduleIdNum
            updateFieldById(extraJobCron.getId(), "scheduleIdNum", scheduleTaskId);
        }

    }

    /**
     * 通用周期任务
     */
    private void addCycleJob(String durationTime, RemindTask remindTask, Boolean isPrev) throws Exception {

        int timeCount = 0;
        int timesCycle = isPrev ? Integer.parseInt(remindTask.getPrevTimesCycle().substring(0, 1))
                : Integer.parseInt(remindTask.getTimesCycle().substring(0, 1));
        String remark = isPrev ? RemindConstant.PRE_TIME_TIP : RemindConstant.CYCLE_TIME_TIP;
        Date nowDate = DateUtil.getSysDate();
        Date startDate = DateUtil.getSysDate();
        if (remindTask.getIsSetEndDate()) {
            if (ObjectUtil.notNull(remindTask.getStartDate()) && startDate.before(remindTask.getStartDate())) {
                startDate = remindTask.getStartDate();
            } else if (ObjectUtil.notNull(remindTask.getEndDate()) && startDate.after(remindTask.getEndDate())) {
                return;
            }
        }

        Date nextDate = remindTask.getNextRemindDateTime(startDate);
        for (int i = 1; i <= timesCycle; i++) {
            Timestamp dateTime = null;
            if (durationTime.endsWith(RemindConstant.TIME_UNIT_CN_NAME_MINUTES)) {
                timeCount = Integer.parseInt(durationTime.replace(RemindConstant.TIME_UNIT_CN_NAME_MINUTES, ""));
                if (isPrev) {
                    timeCount = -timeCount;
                }
                dateTime = cn.hutool.core.date.DateUtil.offsetMinute(nextDate, timeCount * i).toTimestamp();
            } else if (durationTime.endsWith(RemindConstant.TIME_UNIT_CN_NAME_HOUR)) {
                timeCount = Integer.parseInt(durationTime.replace(RemindConstant.TIME_UNIT_CN_NAME_HOUR, ""));
                if (isPrev) {
                    timeCount = -timeCount;
                }
                dateTime = cn.hutool.core.date.DateUtil.offsetHour(nextDate, timeCount * i).toTimestamp();
            } else if (durationTime.endsWith(RemindConstant.TIME_UNIT_CN_NAME_DAY)) {
                timeCount = Integer.parseInt(durationTime.replace(RemindConstant.TIME_UNIT_CN_NAME_DAY, ""));
                if (isPrev) {
                    timeCount = -timeCount;
                }
                dateTime = cn.hutool.core.date.DateUtil.offsetDay(nextDate, timeCount * i).toTimestamp();
            }

            if (remindTask.getIsSetEndDate()) {
                if (remindTask.getEndDate().before(dateTime) || remindTask.getStartDate().after(dateTime)) {
                    continue;
                }
            }
            if (nowDate.after(dateTime)) {
                continue;
            }

            //添加额外的定时提醒的数据
            ExtraJobCron extraJobCron = new ExtraJobCron(null, dateTime, new Timestamp(nextDate.getTime()), remindTask, remark);
            saveOrUpdate(extraJobCron);

            //添加额外的定时提醒的定时任务
            String cronExpress = CronJobUtil.dateToCron(dateTime);
            String scheduleTaskId = cronJobService.addCronJob(cronExpress, remindTask, extraJobCron);

            //更新scheduleIdNum
            updateFieldById(extraJobCron.getId(), "scheduleIdNum", scheduleTaskId);

        }
    }

    public boolean checkIsLastJobOfTask(Long taskId, String nextTime) throws Exception {
        return count(QueryDto.builder()
                .available()
                .addWhere("execute_time > '" + nextTime + "'")
                .addWhere("remind_task_id = " + taskId)
                .build()) == 0;
    }

    public boolean checkIsLastJobOfMensesRemindTask(Long taskId, String nextTime) throws Exception {
        return count(QueryDto.builder()
                .available()
                .addWhere("execute_time > '" + nextTime + "'")
                .addWhere("menses_remind_task_id = " + taskId)
                .build()) == 0;
    }

    public Date queryNextRemindTimeInCurrentCycle(Long taskId) {
        ExtraJobCron extraJobCron = null;
        try {
            extraJobCron = get(QueryDto.builder()
                    .available()
                    .addWhere("remark = '准时提醒'")
                    .addWhere("remind_task_id = " + taskId)
                    .build());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (ObjectUtil.notNull(extraJobCron)) {
            return extraJobCron.getExecuteTime();
        }
        return null;
    }

    public ExtraJobCron getNextExecuteJob(RemindTask remindTask) throws Exception {
        return get(QueryDto.builder()
                .available()
                .addWhere("task_id = " + remindTask.getId())
                .build());
    }

    public ExtraJobCron getNextExecuteJob(MensesRemindTask mensesRemindTask) throws Exception {
        return get(QueryDto.builder()
                .available()
                .addWhere("task_id = " + mensesRemindTask.getId())
                .build());
    }
}
